package com.bestcem.xm.award.service.impl;

import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson.JSON;
import com.bestcem.xm.award.constant.Constants;
import com.bestcem.xm.award.constant.RedisKeyConstant;
import com.bestcem.xm.award.constant.RequestAwardConstants;
import com.bestcem.xm.award.dao.AwardAwardDao;
import com.bestcem.xm.award.entity.pojo.AwardAwardDO;
import com.bestcem.xm.award.enums.AwardStatusEnum;
import com.bestcem.xm.award.enums.AwardTypeEnum;
import com.bestcem.xm.award.grpc.service.param.draw.*;
import com.bestcem.xm.award.grpc.service.param.requestaward.GetRequestAwardParam;
import com.bestcem.xm.award.service.AwardAwardService;
import com.bestcem.xm.award.service.AwardDrawDisplayService;
import com.bestcem.xm.award.service.ProjectUpdatedService;
import com.bestcem.xm.award.service.bo.award.AwardAwardBO;
import com.bestcem.xm.award.service.dto.FileInfoDTO;
import com.bestcem.xm.award.service.dto.awardaward.CreateAwardDTO;
import com.bestcem.xm.award.service.dto.awardaward.DeleteAwardDTO;
import com.bestcem.xm.award.service.dto.awardaward.UpdateAwardDTO;
import com.bestcem.xm.award.service.dto.awardaward.UpdateAwardStatusDTO;
import com.bestcem.xm.award.service.dto.draw.DrawAwardResultDTO;
import com.bestcem.xm.award.service.dto.draw.GetAwardDetailDTO;
import com.bestcem.xm.award.service.dto.draw.RecordDTO;
import com.bestcem.xm.award.service.dto.draw.RecordDetailDTO;
import com.bestcem.xm.award.service.dto.requestaward.RequestAwardDTO;
import com.bestcem.xm.common.core.domain.web.PageResultDTO;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.common.core.uitls.StringUtil;
import com.bestcem.xm.componet.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 奖励外观类 对外提供服务,对内调用不同实现
 *
 * @author sh.pei <sh.pei@idiaoyan.com>
 * @version v1.0
 * @date 2021/7/2 14:26
 */
@Service("AwardDispatcher")
@Slf4j
public class AwardDispatcher extends AbstractAwardAwardService {

    /**
     * 具体奖励类型的服务
     */
    @Autowired
    private List<AwardAwardService> awardAwardServices;

    /**
     * 抽奖服务
     */
    @Resource(name = "DrawAwardServiceImpl")
    private AwardDrawDisplayService drawService;

    /**
     * 新抽奖服务
     */
    @Resource(name = "DrawNewAwardServiceImpl")
    private AwardDrawDisplayService newDrawService;

    @Resource
    private AwardAwardDao awardDao;

    @Autowired
    private ProjectUpdatedService projectUpdatedService;

    @Autowired
    private RedisService redisService;

    /**
     * 奖励类型 和对应的实现类 对应关系
     */
    private HashMap<AwardTypeEnum, AwardAwardService> serviceMap = new HashMap<>();


    public void setAwardAwardServices(List<AwardAwardService> awardAwardServices) {
        this.awardAwardServices = awardAwardServices;
    }

    /**
     * 初始化
     */
    @PostConstruct
    public void loadAllAwardService() {
        for (AwardAwardService awardAwardService : awardAwardServices) {
            if (Objects.nonNull(awardAwardService.getAwardTypeEnum())) {
                serviceMap.put(awardAwardService.getAwardTypeEnum(), awardAwardService);
            }
        }
    }

    /**
     * 创建奖励
     *
     * @param createAwardDTO 创建奖励入参
     * @return 奖励创建结果(id)
     */
    @Override
    public ServiceResult<String> createAward(CreateAwardDTO createAwardDTO) {
        AwardAwardBO awardAward = createAwardDTO.getAwardAward();
        // 创建时不能有id
        createAwardDTO.getAwardAward().setId(null);
        // 所有类型的奖励的通用检查
        ServiceResult<String> result = commonCheckAward(awardAward);
        if (!result.isSuccess()) {
            return result;
        }
        String lockKey = String.format(RedisKeyConstant.AWARD_AWARD_OPERATE, awardAward.getOrgId(), awardAward.getProjectId());
        redisService.lock(lockKey);
        try {
            // 校验奖励数量
            ServiceResult<String> checkAwardCountResult = checkAwardCount(awardAward.getOrgId(), awardAward.getProjectId());
            if (!checkAwardCountResult.isSuccess()) {
                return checkAwardCountResult;
            }
            // 校验名称重复
            ServiceResult<String> checkDuplicateNameResult = checkDuplicateName(awardAward.getName(), awardAward.getProjectId(), null);
            if (!checkDuplicateNameResult.isSuccess()) {
                return checkDuplicateNameResult;
            }
            // 根据奖励类型获取对应实现
            AwardAwardService awardAwardService = getAwardService(awardAward.getTType());
            if (awardAwardService == null) {
                return ServiceResult.fail("找不到对应奖励实现");
            }
            // 具体类型的奖励检查
            result = awardAwardService.independentCheckAward(awardAward);
            if (!result.isSuccess()) {
                return result;
            }
            // 具体的实现类创建奖励
            ServiceResult<String> serviceResult = awardAwardService.createAward(createAwardDTO);
            if (serviceResult.isSuccess()) {
                projectUpdatedService.sendProjectUpdatedMsg(awardAward.getProjectId());
                afterAwardCreate(awardAward.getOrgId(), awardAward.getProjectId(), serviceResult.getData());
            }
            return serviceResult;
        } finally {
            redisService.unlock(lockKey);
        }

    }

    /**
     * 根据奖励类型获取对应的服务实现
     *
     * @param type 奖励类型
     * @return 奖励服务
     */
    public AwardAwardService getAwardService(Integer type) {
        AwardTypeEnum awardType = EnumUtil.getEnumByIndex(AwardTypeEnum.class, type);
        if (awardType == null) {
            return null;
        }
        return serviceMap.get(awardType);
    }

    @Override
    public ServiceResult<String> updateAward(UpdateAwardDTO updateAwardDTO) {
        AwardAwardBO awardAward = updateAwardDTO.getAwardAward();
        if (Objects.isNull(updateAwardDTO.getAwardAward().getId())) {
            return ServiceResult.fail("奖励id为空!");
        }
        // 所有类型的奖励的通用检查
        ServiceResult<String> result = commonCheckAward(awardAward);
        if (!result.isSuccess()) {
            return result;
        }
        String lockKey = String.format(RedisKeyConstant.AWARD_AWARD_OPERATE, awardAward.getOrgId(), awardAward.getProjectId());
        redisService.lock(lockKey);
        try {
            AwardAwardDO oldAward = awardAwardDao.findByAwardIdAndStatus(updateAwardDTO.getAwardAward().getId(), AwardStatusEnum.INACTIVE.getIndex());
            if (oldAward == null) {
                return ServiceResult.fail("奖励不存在或非未激活状态!");
            }
            // 校验名称重复
            ServiceResult<String> checkDuplicateNameResult = checkDuplicateName(awardAward.getName(), awardAward.getProjectId(), awardAward.getId());
            if (!checkDuplicateNameResult.isSuccess()) {
                return checkDuplicateNameResult;
            }
            // 根据奖励类型获取对应实现
            AwardAwardService awardAwardService = getAwardService(awardAward.getTType());
            if (awardAwardService == null) {
                return ServiceResult.fail("找不到对应奖励实现!");
            }
            // 防止数据覆盖
            updateAwardDTO.getAwardAward().setActivityId(oldAward.getActivityId());
            updateAwardDTO.getAwardAward().setStatus(AwardStatusEnum.INACTIVE.getIndex());
            updateAwardDTO.getAwardAward().setCreatedDate(oldAward.getCreatedDate());
            // 具体类型的奖励检查
            result = awardAwardService.independentCheckAward(awardAward);
            if (!result.isSuccess()) {
                return result;
            }
            // 具体类型的奖励更新
            ServiceResult<String> serviceResult = awardAwardService.updateAward(updateAwardDTO);
            if (serviceResult.isSuccess()) {
                projectUpdatedService.sendProjectUpdatedMsg(awardAward.getProjectId());
            }
            return serviceResult;
        } finally {
            redisService.unlock(lockKey);
        }
    }


    @Override
    public ServiceResult<Integer> deleteAward(DeleteAwardDTO deleteAwardDTO) {
        // 查询奖励
        AwardAwardDO awardAwardDO = awardAwardDao.findById(deleteAwardDTO.getAwardId());
        if (awardAwardDO == null || !StringUtil.equalsIgnoreCase(awardAwardDO.getOrgId(), deleteAwardDTO.getOrgId())) {
            return ServiceResult.fail("奖励不存在！");
        }
        String lockKey = String.format(RedisKeyConstant.AWARD_CHANGE_STATUS, awardAwardDO.getId());
        redisService.lock(lockKey);
        try {
            AwardAwardService awardAwardService = getAwardService(awardAwardDO.getTType());
            if (awardAwardService == null) {
                return ServiceResult.fail("找不到对应奖励实现！");
            }
            // 检查是否可以删除
            ServiceResult<String> result = awardAwardService.checkDeleteAward(awardAwardDO);
            if (!result.isSuccess()) {
                return ServiceResult.fail(result.getMsg());
            }
            // 设置红包活动id
            deleteAwardDTO.setActivityId(awardAwardDO.getActivityId());
            // 具体类型奖励的服务删除
            ServiceResult<Integer> deleteResult = awardAwardService.deleteAward(deleteAwardDTO);
            if (deleteResult.isSuccess()) {
                // 发送项目更新mq
                projectUpdatedService.sendProjectUpdatedMsg(awardAwardDO.getProjectId());
                afterAwardDelete(awardAwardDO.getOrgId(), awardAwardDO.getProjectId(), awardAwardDO.getId());

            }
            return deleteResult;

        } finally {
            redisService.unlock(lockKey);
        }
    }

    /**
     * 更新奖励状态
     *
     * @param updateAwardStatusDTO 请求参数
     * @return 更新结果
     */
    @Override
    public ServiceResult<String> updateAwardStatus(UpdateAwardStatusDTO updateAwardStatusDTO) {
        // 根据id查询奖励
        AwardAwardDO awardAwardDO = awardAwardDao.findById(updateAwardStatusDTO.getAwardId());
        if (awardAwardDO == null || !StringUtil.equalsIgnoreCase(awardAwardDO.getOrgId(), updateAwardStatusDTO.getOrgId())) {
            return ServiceResult.fail("奖励不存在");
        }
        // 检查状态是否一致 一致的话直接成功
        if (updateAwardStatusDTO.getStatus() == awardAwardDO.getStatus().intValue()) {
            return ServiceResult.success(awardAwardDO.getId());
        }
        String lockKey = String.format(RedisKeyConstant.AWARD_CHANGE_STATUS, awardAwardDO.getId());
        redisService.lock(lockKey);
        try {
            // 调用具体奖励业务更新奖励状态
            updateAwardStatusDTO.setOldStatus(awardAwardDO.getStatus());
            AwardAwardService awardAwardService = getAwardService(awardAwardDO.getTType());
            if (awardAwardService == null) {
                return ServiceResult.fail("找不到对应奖励实现");
            }
            ServiceResult<String> serviceResult = awardAwardService.updateAwardStatus(updateAwardStatusDTO);
            if (serviceResult.isSuccess()) {
                projectUpdatedService.sendProjectUpdatedMsg(awardAwardDO.getProjectId());
            }
            return serviceResult;
        } finally {
            redisService.unlock(lockKey);
        }
    }


    @Override
    public AwardTypeEnum getAwardTypeEnum() {
        return null;
    }

    @Override
    public ServiceResult<String> requestAward(GetRequestAwardParam getRequestAwardParam) {
        // 通用校验
        RequestAwardDTO requestAwardDTO = super.commonValid(getRequestAwardParam);
        if (log.isInfoEnabled()) {
            log.info("请求奖励参数转换,转换前:{},转换后:{}", JSON.toJSONString(getRequestAwardParam), JSON.toJSONString(requestAwardDTO));
        }
        if (Validator.isFalse(requestAwardDTO.getStatus())) {
            return ServiceResult.fail(requestAwardDTO.getMessage());
        }
        // 奖励类型
        Integer tType = Optional.of(requestAwardDTO).map(RequestAwardDTO::getAwardAwardDO)
                .map(AwardAwardDO::getTType).orElse(Constants.MINUS_ONE);
        AwardAwardService awardAwardService = this.getAwardService(tType);
        return Validator.isNull(awardAwardService)
                ? ServiceResult.fail(RequestAwardConstants.AWARD_TYPE_ERROR)
                : awardAwardService.doRequestAward(requestAwardDTO);
    }

    /**
     * 修改中奖状态
     *
     * @param param {@link UpdateStatusParam} 请求参数
     * @return 修改结果(条数)
     */
    public ServiceResult<Long> updateWinnerStatus(UpdateStatusParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.updateWinnerStatus(param);
        } else {
            return newDrawService.updateWinnerStatus(param);
        }
    }

    /**
     * 短信验证码校验
     *
     * @param param 请求参数
     * @return 校验结果
     */
    public ServiceResult verifySmsAuthCode(VerifySmsCodeParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.verifySmsAuthCode(param);
        } else {
            return newDrawService.verifySmsAuthCode(param);
        }
    }

    /**
     * 获取中奖名单
     *
     * @param param 请求参数
     * @return 分页抽奖记录
     */
    public ServiceResult<PageResultDTO<RecordDTO>> listWinners(GetWinnersParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.listWinners(param);
        } else {
            return newDrawService.listWinners(param);
        }
    }

    /**
     * 获取中奖名单奖项信息
     *
     * @param param 请求参数
     * @return 分页查询结果
     */
    public ServiceResult<PageResultDTO<RecordDetailDTO>> listWinnerDetails(ListWinnerDetailsParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.listWinnerDetails(param);
        } else {
            return newDrawService.listWinnerDetails(param);
        }
    }

    /**
     * 中奖名单导出
     *
     * @param param 请求参数
     * @return 导出结果(oss文件信息)
     */
    public ServiceResult<FileInfoDTO> exportWinners(ExportWinnersParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.exportWinners(param);
        } else {
            return newDrawService.exportWinners(param);
        }
    }

    /**
     * 中奖名单奖项信息导出
     *
     * @param param 请求参数
     * @return 导出结果(oss文件信息)
     */
    public ServiceResult<FileInfoDTO> exportWinnerDetails(ExportWinnerDetailsParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.exportWinnerDetails(param);
        } else {
            return newDrawService.exportWinnerDetails(param);
        }
    }

    /**
     * 获取抽奖详情
     *
     * @param param 请求参数
     * @return 抽奖详情信息
     */
    public ServiceResult<GetAwardDetailDTO> getAwardDetail(GetAwardDetailParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.getAwardDetail(param);
        } else {
            return newDrawService.getAwardDetail(param);
        }
    }

    /**
     * 抽奖
     *
     * @param param 请求参数
     * @return 抽奖结果
     */
    public ServiceResult<DrawAwardResultDTO> drawAward(DrawAwardParam param) {
        ServiceResult<Integer> checkResult = checkDrawAwardType(param.getAwardId());
        if (!checkResult.isSuccess()) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        if (checkResult.getData() == AwardTypeEnum.DRAW.getIndex()) {
            return drawService.drawAward(param);
        } else {
            return newDrawService.drawAward(param);
        }
    }

    /**
     * 判断抽奖奖励类型
     * @param awardId 奖励id
     * @return 判断结果
     */
    private ServiceResult<Integer> checkDrawAwardType(String awardId) {
        AwardAwardDO award = awardDao.findById(awardId);
        if (Objects.isNull(award)) {
            return ServiceResult.fail("奖励不存在");
        } else if (Objects.isNull(award.getTType())) {
            return ServiceResult.fail("奖励类型缺失");
        } else if (award.getTType() == AwardTypeEnum.DRAW.getIndex()) {
            return ServiceResult.success(AwardTypeEnum.DRAW.getIndex());
        } else if (award.getTType() == AwardTypeEnum.NEW_DRAW.getIndex()) {
            return ServiceResult.success(AwardTypeEnum.NEW_DRAW.getIndex());
        } else {
            return ServiceResult.fail("奖励类型错误");
        }
    }
}
